package com.cskaoyan.javase.oop2._1extends._7initialization._0introduction;
/**
 * 何为子类对象初始化问题?
 * 我们先复习一下我们认识引用数据类型的过程：
 * 首先我们从数组对象的初始化开始，我们知道引用数据类型变量分为两个部分，在堆上创建对象，在栈中创建引用，引用通过存储地址指向堆上的对象。对象是引用数据类型的实质，但是我们不能直接访问对象，而是通过栈上的引用间接访问堆上的对象。
 *
 * 然后我们学习了对象与类的概念，引入了类加载的概念。并且使用数据类型的定义统一了引用数据类型和基本数据类型：
 * 数据类型：一组数据 + 基于数据的一组合法操作
 * 引用数据类型：成员变量的合集 + 成员方法的合集
 * 也就是说，引用数据类型（类）是特殊的数据类型，是自定义的数据类型。这种自定义的数据类型JVM不认识它，需要一个了解认识它的过程，称之为类加载。
 * JVM通过类加载了解一个类型，从而能够完成创建对象，或者其它的一些操作。
 *
 * 现在我们学习了继承的概念，在继承中，子类会继承父类的成员，子类对象可以访问父类成员。那么问题就来了：
 * 父类的成员从何而来？父类的成员变量的赋值顺序是怎么样的？结果是怎么样的?等等
 * 以上问题就是子类对象的初始化问题，即研究子类对象创建时，父类的成员从何而来，相关成员变量的赋值等相关的问题。
 *
 * 引例：
 * 我们通过一个引例，练习来讲解这个问题：
 * 创建一个Person类，有name属性和eat方法
 * 创建一个Star类，有stageName属性和sing方法
 * Star类要继承Person类
 * 创建Star类对象，画出这个过程的内存图，描述子类对象初始化的过程。
 *
 * 1.首先是类加载，创建子类对象，或者触发子类的类加载，应该先类加载它的父类
 *      而且如果父类还有父类，那么会优先类加载顶层父类
 *      从顶层父类（Object）开始类加载，直到类加载完毕子类。
 *      当然Object类的类加载对程序没有影响，不用考虑它。
 *
 *      总结目前类加载的时机:
 *          1.main方法启动
 *          2.访问静态成员
 *          3.new对象
 *          4.触发子类类加载,先类加载父类
 *
 * 2.类加载结束后,开始创建对象,要明确只会创建一个对象,就是子类对象,不会创建父类对象.
 * 3.子类对象当中需要开辟空间存储父类的成员变量和自身的成员变量，这些成员变量不是笼统的放在一起。而是单独开辟一片独属于直接父类的区域，用于存放父类的成员变量。
 *
 * 之前，我们讲过，在构造器或成员方法中隐含this传参指向当前对象，那么这里子类对象中父类那片独立区域就使用super引用指向。并且这片专属于父类成员的区域，由于可以近似看成一个父类对象， 所以也可以认为super指向当前对象的父类对象。由于这个特点，super可以在子类构造器或成员方法中，用于调用父类构造器或者访问父类成员。
 *
 * 4.这时子类对象大体上就创建完毕了，只剩下最后一个问题：
 *      父类的成员变量如何赋值？赋值的顺序是什么样的？赋值的结果是什么样的？
 * 它们的默认初始化具有默认值的过程，不需要去考虑它们的先后顺序（实际上在对象的成员变量的其他赋值方式开始赋值时，它们已经具有了默认值)
 *
 * 5.子类成员变量和父类成员变量的赋值先后顺序是"先父后子"的
 *
 * 6.为什么子类成员变量和父类成员变量的赋值顺序是"先父后子"的呢?
 *      思考:如果能够保证父类的构造器在子类构造器之前执行,那么父类的所有赋值手段,都会在子类所有赋值手段之前执行.
 *      很显然,这个猜测是正确的,因为父类的构造代码块会先于子类构造代码块执行,这意味着父类构造器先于子类执行.
 *
 * 7.为什么父类构造器会先于子类构造器执行?
 *      this(实参列表)表示调用自身的其他构造器
 *      猜测:
 *          super(实参列表)可能能够调用父类的构造器
 *          所以猜想子类构造器的第一行如果存在super调用父类构造器,就能够保证构造器"先父后子"执行!!!
 *
 * 8.两种子类对象初始化的方式:
 *      a.子类对象的隐式初始化:在java当中,任何一个类的任何构造器中(包括默认提供的无参构造器),只要第一行没有使用this或者super去调用其他构造器,永远隐含一行代码:
 *      super();
 *      表示调用父类的无参构造器
 *      这样子类对象在初始化时,永远先执行父类构造器,父类成员变量的赋值,永远先于子类
 *
 *      b.显然子类对象的隐式初始化调用父类的无参构造器,无法给父类成员变量赋值
 *          如果在子类创建时,有给父类成员赋值的需求,就必须显式的调用父类的有参构造器
 *          这样就必须在子类构造器的第一行,用:
 *              super(实参列表)
 *              去调用父类的有参构造器
 *          这种子类创建的方式叫做子类对象的显式初始化
 *
 * @since 14:27
 * @author wuguidong@cskaoyan.onaliyun.com
 */
public class Demo {
    public static void main(String[] args) {
        // Star s = new Star();
        // System.out.println(s.stageName);

        Star s = new Star("李四");
        System.out.println(s.stageName);

        Star s2 = new Star("麻子", "长风");
        System.out.println(s2.stageName);
    }
}
class Person{
    /*static {
        System.out.println("Person类加载！");
    }*/
    /*{
        System.out.println("Person类的构造代码块!");
    }*/
    String name = "张三";
    public void eat(){
        System.out.println("我想吃饭！");
    }

    public Person(String name) {
        this.name = name;
    }

    public Person() {
        System.out.println("Person类的无参构造器");
    }
}
class Star extends Person{
    /*static {
        System.out.println("Star类加载！");
    }*/
    /*{
        System.out.println("Star类的构造代码块!");
    }*/
    // 艺名
    String stageName = name + "的朋友";
    public void sing(){
        System.out.println("来一首爱的供养！");
    }

    public Star() {
        System.out.println("Star类的无参构造器");
    }

    public Star(String name){
        super(name);
    }

    public Star(String name,String stageName){
        super(name);
        this.stageName = stageName;
    }
}